Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds a "create-cloud" init mode, resolves init mode from flags or prompts, centralizes auth/session and env-key writing, enables interactive cloud project creation when none exist, prevents unsafe local config overwrites in non-interactive mode, and moves interactive project-creation into a shared helper. Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as Client (CLI)
participant Auth as Auth Service (Auth)
participant API as Stack API (API)
participant FS as Filesystem (.env)
rect rgba(135,206,250,0.5)
CLI->>Auth: ensureLoggedInSession() (prompt/login if needed)
Auth-->>CLI: session/token
end
rect rgba(144,238,144,0.5)
CLI->>API: listOwnedProjects(session)
API-->>CLI: projects[]
alt no owned projects (interactive)
CLI->>CLI: prompt create vs cancel
CLI->>API: createProjectInteractively(session, prompts)
API-->>CLI: project (with internal keys)
else has projects or link chosen
CLI->>API: select/link existing project
API-->>CLI: project (with keys)
end
end
rect rgba(255,182,193,0.5)
CLI->>FS: writeProjectKeysToEnv(project.keys)
FS-->>CLI: .env written
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR restores two previously commented-out interactive
Confidence Score: 3/5Mostly safe to merge for interactive use, but the "link" mode value mismatch can break non-interactive invocations silently. One P1 finding: the "link" value returned by the interactive select is not in the InitOptions.mode type, so --mode link passes the non-interactive guard but then hangs waiting for a second prompt. The rest of the change is a straightforward restoration of commented-out UX code. packages/stack-cli/src/commands/init.ts — specifically the InitOptions type definition and the hasFlags guard logic. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[stack init] --> B{opts.mode set?}
B -- yes --> C[hasFlags = true\nskip non-interactive check]
B -- no --> D{isNonInteractiveEnv?}
D -- yes --> E[throw CliError]
D -- no --> F["select: link | create"]
C --> G[mode = opts.mode]
F --> G
G --> H{mode === link\nor link-config\nor link-cloud?}
H -- yes --> I[handleLink]
H -- mode === create --> J[handleCreate]
H -- else --> K[throw CliError: Unknown mode]
I --> L{opts.mode === link-config?}
L -- yes --> M[source = config-file]
L -- no --> N{opts.mode === link-cloud?}
N -- yes --> O[source = cloud]
N -- no --> P["select: config-file | cloud\n⚠️ requires interactive terminal"]
M --> Q[handleLinkFromConfigFile]
O --> R[handleLinkFromCloud]
P --> R
P --> Q
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/stack-cli/src/commands/init.ts (1)
60-66: Consider dropping the explicit: stringannotation to preserve type narrowing.Annotating
modeasstringwidens away the useful union. With inference,modewould be typed as"create" | "link-config" | "link-cloud" | "link"(fromopts.mode∪ the prompt'svalueliterals), which lets TypeScript exhaustively verify theif/else if/elsechain at lines 70–78 and catches typos at compile time. With: string, thethrow new CliError(Unknown mode: ${mode})on line 77 is the only guard against mistakes.♻️ Proposed refactor
- const mode: string = opts.mode ?? await select({ + const mode = opts.mode ?? await select({ message: "Would you like to link to an existing project, or create a new one?", choices: [ { name: "Link an existing project", value: "link" as const }, { name: "Create a new project (local emulator)", value: "create" as const }, ], });As per coding guidelines: "Do NOT use
as/any/type casts or anything else like that to bypass the type system."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/stack-cli/src/commands/init.ts` around lines 60 - 66, Remove the explicit ": string" annotation on the const mode declaration so TypeScript can infer the union type from opts.mode and the select() choices; change "const mode: string = opts.mode ?? await select(...)" to "const mode = opts.mode ?? await select(...)" (leave the select choices as "value: 'link' as const" etc.) so subsequent branches that check mode (the if/else chain handling "create" | "link" | "link-config" | "link-cloud") are exhaustively type-checked by the compiler.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 60-66: Remove the explicit ": string" annotation on the const mode
declaration so TypeScript can infer the union type from opts.mode and the
select() choices; change "const mode: string = opts.mode ?? await select(...)"
to "const mode = opts.mode ?? await select(...)" (leave the select choices as
"value: 'link' as const" etc.) so subsequent branches that check mode (the
if/else chain handling "create" | "link" | "link-config" | "link-cloud") are
exhaustively type-checked by the compiler.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2958fdc2-8c31-401a-8bf9-3a2648b24abd
📒 Files selected for processing (1)
packages/stack-cli/src/commands/init.ts
Adds the ability to create a project using cli.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
packages/stack-cli/src/commands/init.ts (2)
280-281: Avoid the non-null assertion; use?? throwErr(...).
projects.find(...)!bypasses the type system. Although each branch above ensuresprojectIdexists inprojects, a future refactor could break that invariant silently. As per coding guidelines, "Code defensively. Prefer?? throwErr(...)over non-null assertions, with good error messages explicitly stating the assumption that must've been violated".♻️ Proposed fix
- const project = projects.find((p) => p.id === projectId)!; + const project = projects.find((p) => p.id === projectId) + ?? throwErr(`Resolved projectId '${projectId}' is not present in the owned projects list`);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/stack-cli/src/commands/init.ts` around lines 280 - 281, Replace the unsafe non-null assertion on the found project: instead of const project = projects.find((p) => p.id === projectId)!;, use a null-check pattern with the project's result combined with the project's error helper (e.g., projects.find(... ) ?? throwErr(...)) so that if the project is not found you throw a descriptive error; update the code that passes project to writeProjectKeysToEnv to use this defensively-checked value and include an explicit message naming projectId and the failed assumption.
172-175: ImportAdminOwnedProjectfrom@stackframe/jsinstead of duplicating the type signature inline.The inline type for the
projectparameter replicates the structure ofAdminOwnedProject(both callers pass objects fromuser.createProject()anduser.listOwnedProjects()). Inlining thecreateInternalApiKeymethod signature means SDK changes (new options, return fields) will silently diverge from this helper's type, reducing type safety.♻️ Proposed refactor
-import { StackClientApp } from "@stackframe/js"; +import { StackClientApp, type AdminOwnedProject } from "@stackframe/js";-async function writeProjectKeysToEnv( - project: { id: string, app: { createInternalApiKey: (opts: { description: string, expiresAt: Date, hasPublishableClientKey: boolean, hasSecretServerKey: boolean, hasSuperSecretAdminKey: boolean }) => Promise<{ publishableClientKey?: string | null, secretServerKey?: string | null }> } }, - outputDir: string, -) { +async function writeProjectKeysToEnv(project: AdminOwnedProject, outputDir: string) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/stack-cli/src/commands/init.ts` around lines 172 - 175, Replace the inline project parameter type in writeProjectKeysToEnv with the exported AdminOwnedProject type from `@stackframe/js`: import AdminOwnedProject and change the function signature to accept project: AdminOwnedProject so it uses the SDK's canonical shape (this ensures createInternalApiKey's options/return types stay in sync with user.createProject() and user.listOwnedProjects()); update any places that reference the inline type accordingly and run typecheck to catch any required import/export adjustments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 62-78: runInit computes a resolved mode into the local variable
mode but handleLink still reads opts.mode, causing flags like
--config-file/--select-project-id to be ignored; fix by making handleLink use
the resolved mode: either pass mode as an explicit argument to handleLink (e.g.,
handleLink(opts, mode)) or set opts.mode = mode immediately before calling
handleLink so handleLink sees the inferred value; update handleLink's
signature/usage accordingly (and remove the now-dead "link" branch in any
switch/if that checks for mode === "link" since runInit never assigns that
value).
- Around line 184-189: The env assembly in envLines silently falls back to empty
strings for apiKey.publishableClientKey and apiKey.secretServerKey; instead
validate those fields after createInternalApiKey and throw if missing (e.g.,
using your throwErr helper or throw new Error) so you never write a broken .env.
Locate the code around createInternalApiKey and the envLines constant and assert
that apiKey.publishableClientKey and apiKey.secretServerKey are
non-null/undefined (throwing with a clear message mentioning
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY / STACK_SECRET_SERVER_KEY and
project.id) before interpolating them into envLines.
In `@packages/stack-cli/src/lib/create-project.ts`:
- Around line 27-35: The code unconditionally picks teams[0] after calling
user.listTeams() in create-project.ts, so users in multiple teams cannot choose
ownership; update the createProject flow to either accept a teamId on
CreateProjectOptions and pass it into user.createProject (propagate a --team-id
flag to CLI commands like init/project create), or when interactive and
teams.length > 1 prompt the user to select a team before calling
user.createProject; reference user.listTeams(), the teams array, and
user.createProject({ displayName, teamId }) to locate where to add the prompt or
hook up the new CreateProjectOptions.teamId parameter and use it instead of
teams[0].id.
---
Nitpick comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 280-281: Replace the unsafe non-null assertion on the found
project: instead of const project = projects.find((p) => p.id === projectId)!;,
use a null-check pattern with the project's result combined with the project's
error helper (e.g., projects.find(... ) ?? throwErr(...)) so that if the project
is not found you throw a descriptive error; update the code that passes project
to writeProjectKeysToEnv to use this defensively-checked value and include an
explicit message naming projectId and the failed assumption.
- Around line 172-175: Replace the inline project parameter type in
writeProjectKeysToEnv with the exported AdminOwnedProject type from
`@stackframe/js`: import AdminOwnedProject and change the function signature to
accept project: AdminOwnedProject so it uses the SDK's canonical shape (this
ensures createInternalApiKey's options/return types stay in sync with
user.createProject() and user.listOwnedProjects()); update any places that
reference the inline type accordingly and run typecheck to catch any required
import/export adjustments.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5ac9f04c-93fb-4786-8c1c-8bdda2e99d3c
📒 Files selected for processing (3)
packages/stack-cli/src/commands/init.tspackages/stack-cli/src/commands/project.tspackages/stack-cli/src/lib/create-project.ts
…PI keys - handleLink now respects the mode resolved by runInit (from --config-file / --select-project-id), so those flags skip the interactive source prompt. - writeProjectKeysToEnv throws via throwErr instead of silently writing empty publishable/secret keys to .env when the API returns null.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/stack-cli/src/commands/init.ts (1)
272-273: Use an explicit null check forautoCreatedProjectId.
autoCreatedProjectIdisstring | null, soelse if (autoCreatedProjectId)works, but the repo convention is explicit nullishness checks for clarity.♻️ Proposed fix
- } else if (autoCreatedProjectId) { + } else if (autoCreatedProjectId != null) { projectId = autoCreatedProjectId;As per coding guidelines: "Unless very clearly equivalent from types, prefer explicit null/undefinedness checks over boolean checks, eg.
foo == nullinstead of!foo."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/stack-cli/src/commands/init.ts` around lines 272 - 273, Replace the truthy check on autoCreatedProjectId with an explicit null/undefined check: change the condition that currently reads else if (autoCreatedProjectId) to else if (autoCreatedProjectId != null) (or ==/!= variant per project style) so projectId is assigned only when autoCreatedProjectId is not null/undefined; update the branch around the projectId and autoCreatedProjectId variables accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 283-285: Replace the non-null assertion on the result of
projects.find with a defensive check that throws a clear error: change the
expression that assigns project (the one using projects.find((p) => p.id ===
projectId)!) to use the nullish-coalescing pattern with throwErr(...) instead
(e.g., projects.find(...) ?? throwErr(...)); include a concise message stating
the invariant that projectId must exist in projects; ensure this updated project
variable is then passed to writeProjectKeysToEnv unchanged.
---
Nitpick comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 272-273: Replace the truthy check on autoCreatedProjectId with an
explicit null/undefined check: change the condition that currently reads else if
(autoCreatedProjectId) to else if (autoCreatedProjectId != null) (or ==/!=
variant per project style) so projectId is assigned only when
autoCreatedProjectId is not null/undefined; update the branch around the
projectId and autoCreatedProjectId variables accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d521d7f9-c9e6-4020-a72c-6f7d86b716e5
📒 Files selected for processing (1)
packages/stack-cli/src/commands/init.ts
- Restore interactive "link existing" option alongside create - Validate incompatible flag combos (e.g. --apps with --mode create-cloud) - Fail upfront if --output-dir does not exist, avoiding orphan projects - Prompt before overwriting an existing stack.config.ts - Tighten mode union and convert dispatch to exhaustive switch
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/stack-cli/src/commands/init.ts (1)
38-42:⚠️ Potential issue | 🟡 MinorNon-interactive guard rejects
--config-file/--select-project-iddespite inference.
hasFlagsonly inspectsopts.mode, butrunInitinfers the mode fromopts.selectProjectId(→link-cloud) andopts.configFile(→link-config) at lines 97–100. As a result, a CI invocation likestack init --config-file ./stack.config.ts(or--select-project-id <id>) trips the guard and errors out with"stack init requires an interactive terminal. Use --mode flag for non-interactive usage."even though those flags fully determine the flow. Users are then forced to redundantly pass--mode link-config/--mode link-cloudfor inference to ever take effect.🐛 Proposed fix
- const hasFlags = opts.mode != null; + const hasFlags = opts.mode != null || opts.selectProjectId != null || opts.configFile != null;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/stack-cli/src/commands/init.ts` around lines 38 - 42, The non-interactive guard only checks hasFlags based on opts.mode, but runInit infers mode from opts.selectProjectId and opts.configFile (see runInit and lines where mode is derived), so update the guard to treat --select-project-id and --config-file as non-interactive flags too; either expand hasFlags to (opts.mode != null || opts.selectProjectId != null || opts.configFile != null) or run the same inference logic used in runInit to determine mode before enforcing isNonInteractiveEnv() so CI calls like `stack init --config-file` / `--select-project-id` are allowed without an explicit --mode.
♻️ Duplicate comments (1)
packages/stack-cli/src/commands/init.ts (1)
334-335:⚠️ Potential issue | 🟡 MinorReplace the
!non-null assertion with?? throwErr(...).
projects.find(...)is typed asProject | undefined. By constructionprojectIdis one of: from--select-project-id(validated againstprojectson line 317–320), theautoCreatedProjectId(just appended on line 311), or selected via the prompt — so the assumption holds, but the codebase convention is to encode it defensively rather than silence the type system.🛡️ Proposed fix
- const project = projects.find((p) => p.id === projectId)!; + const project = projects.find((p) => p.id === projectId) + ?? throwErr(`Selected project '${projectId}' unexpectedly missing from owned projects list`); await writeProjectKeysToEnv(project, outputDir);As per coding guidelines: "Code defensively. Prefer
?? throwErr(...)over non-null assertions, with good error messages explicitly stating the assumption that must've been violated."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/stack-cli/src/commands/init.ts` around lines 334 - 335, The code uses a non-null assertion when resolving the selected project: replace "const project = projects.find((p) => p.id === projectId)!" with a defensive check using the helper throwErr so you don't silence the type system; i.e., call projects.find((p) => p.id === projectId) and if it returns undefined invoke throwErr with a clear message referencing projectId and the expectation (e.g., "selected projectId not found in projects"), then pass the resulting project to writeProjectKeysToEnv; update any import or reference to throwErr if needed.
🧹 Nitpick comments (1)
packages/stack-cli/src/commands/init.ts (1)
57-57: Use explicit nullness checks for option flags (coding guideline).Multiple sites in this file use truthy checks on string-valued options where the guideline calls for explicit
!= null/== null:
- Line 57:
if (opts.selectProjectId && opts.configFile)- Line 73:
if (opts.mode)- Line 95:
if (opts.mode), lines 97/99:else if (opts.selectProjectId) / (opts.configFile)- Line 316:
if (opts.selectProjectId), line 322:else if (autoCreatedProjectId)Aside from style, behavior subtly differs for empty strings (e.g.,
--apps ""on line 371): a truthy check treats""as "not provided", whereas!= nulltreats it as "provided but empty". The latter is what the guideline mandates and is also what makesvalidateOptionsconsistent — note line 75 already usesopts[key] != null, so the file is internally inconsistent.As per coding guidelines: "Unless very clearly equivalent from types, prefer explicit null/undefinedness checks over boolean checks, eg.
foo == nullinstead of!foo."Also applies to: 73-73, 95-99, 316-322
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/stack-cli/src/commands/init.ts` at line 57, Replace truthy checks with explicit null/undefined checks for the option flags to preserve empty-string semantics: change conditionals that read "if (opts.selectProjectId && opts.configFile)" and the other occurrences "if (opts.mode)" / "else if (opts.selectProjectId) / (opts.configFile)" and "if (opts.selectProjectId)" / "else if (autoCreatedProjectId)" so they use explicit != null (or == null where appropriate) on the specific properties (opts.selectProjectId, opts.configFile, opts.mode, autoCreatedProjectId) to detect presence versus absence; update all referenced branches in the functions around those checks so behavior for empty string values remains "provided but empty" and consistent with validateOptions which already uses opts[key] != null.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 207-221: The try/catch in ensureLoggedInSession is too broad;
narrow the risky call to only resolveSessionAuth so we don't catch unrelated
errors. Call resolveSessionAuth inside a small try block, catch AuthError there,
handle non-interactive case via isNonInteractiveEnv -> throw CliError, otherwise
call performLogin(flags) and then re-run resolveSessionAuth to obtain
sessionAuth; finally return that sessionAuth and rethrow any non-AuthError
exceptions. Ensure you reference the resolveSessionAuth, AuthError,
performLogin, isNonInteractiveEnv, and CliError symbols when making the change.
---
Outside diff comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 38-42: The non-interactive guard only checks hasFlags based on
opts.mode, but runInit infers mode from opts.selectProjectId and opts.configFile
(see runInit and lines where mode is derived), so update the guard to treat
--select-project-id and --config-file as non-interactive flags too; either
expand hasFlags to (opts.mode != null || opts.selectProjectId != null ||
opts.configFile != null) or run the same inference logic used in runInit to
determine mode before enforcing isNonInteractiveEnv() so CI calls like `stack
init --config-file` / `--select-project-id` are allowed without an explicit
--mode.
---
Duplicate comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 334-335: The code uses a non-null assertion when resolving the
selected project: replace "const project = projects.find((p) => p.id ===
projectId)!" with a defensive check using the helper throwErr so you don't
silence the type system; i.e., call projects.find((p) => p.id === projectId) and
if it returns undefined invoke throwErr with a clear message referencing
projectId and the expectation (e.g., "selected projectId not found in
projects"), then pass the resulting project to writeProjectKeysToEnv; update any
import or reference to throwErr if needed.
---
Nitpick comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Line 57: Replace truthy checks with explicit null/undefined checks for the
option flags to preserve empty-string semantics: change conditionals that read
"if (opts.selectProjectId && opts.configFile)" and the other occurrences "if
(opts.mode)" / "else if (opts.selectProjectId) / (opts.configFile)" and "if
(opts.selectProjectId)" / "else if (autoCreatedProjectId)" so they use explicit
!= null (or == null where appropriate) on the specific properties
(opts.selectProjectId, opts.configFile, opts.mode, autoCreatedProjectId) to
detect presence versus absence; update all referenced branches in the functions
around those checks so behavior for empty string values remains "provided but
empty" and consistent with validateOptions which already uses opts[key] != null.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8ac51279-18dd-4474-bb98-87bfcfe97442
📒 Files selected for processing (1)
packages/stack-cli/src/commands/init.ts
|
Promptless prepared a documentation update related to this change. Triggered by PR #1379 Added comprehensive CLI init options reference to the setup guide, documenting the new Review: Document CLI init options |
Summary by CodeRabbit
New Features
Behavior Changes
Refactor